#include "WorldSpace.h"
#include "WorldCell.h"

#include "../../Math/Vec3f.h"

#include <iostream>

using namespace World;

WorldSpace::WorldSpace()
{
}

WorldSpace::~WorldSpace()
{
}

void WorldSpace::render()
{
	for(int x = 0; x < data->nbCells.x(); x++)
	for(int y = 0; y < data->nbCells.y(); y++)
	{
		data->cells[x][y]->render();
	}
}

void WorldSpace::generate(const TCODParserPtr &cfg_parser, const std::string &cfg_path)
{
	data = std::unique_ptr<WorldSpaceData>(new WorldSpaceData());

	//First we extract the settings of this world from file
	auto world_type = cfg_parser->newStructure("world_type");
	world_type->addProperty("seed", TCOD_TYPE_INT, true);
	world_type->addProperty("width", TCOD_TYPE_INT, true);
	world_type->addProperty("height", TCOD_TYPE_INT, true);
	world_type->addProperty("waterType", TCOD_TYPE_INT, false);
	world_type->addProperty("waterHeight", TCOD_TYPE_FLOAT, false);
	world_type->addFlag("canFastTravel");
	world_type->addStructure(world_type);
	cfg_parser->run(cfg_path.c_str(), new WorldCfgParser(data));

	//Then we use the extracted data to randomly generate the world
	data->ran = std::unique_ptr<TCODRandom>(new TCODRandom(data->seed));
	data->noise = std::unique_ptr<TCODNoise>(new TCODNoise(2, data->ran.get()));
	data->heightmap = std::unique_ptr<TCODHeightMap>(new TCODHeightMap(data->nbCells.x(), data->nbCells.y()));
	
	int rand_coefs_num = data->ran->getInt(1, 50);
	std::vector<float> coefs(rand_coefs_num);
	for(int i = 0; i < rand_coefs_num; i++)
	{
		coefs[i] = data->ran->getFloat(0.05f, 100.0f);
	}

	data->heightmap->addVoronoi(64, rand_coefs_num, &coefs[0], data->ran.get());
	data->heightmap->scale(-1.0f);
	data->heightmap->addFbm(data->noise.get(), 1.0195f, 1.0295f, 0.0f, 0.0f, 8.0f, 0.0f, 10000.0f);
	data->heightmap->addFbm(data->noise.get(), 3.95f, 0.0295f, 0.0f, 0.0f, 4.0f, 0.0f, 1000.0f);
	data->heightmap->normalize(-1.0f, 1.0f);
	data->heightmap->scale(220.0f);

	//Resize cells map
	data->cells.resize(data->nbCells.x());
	for(auto &cell_y : data->cells)
		cell_y.resize(data->nbCells.y());

	//Split the generated world into world cells
	for(int x = 0; x < data->nbCells.x(); x++)
	for(int y = 0; y < data->nbCells.y(); y++)
	{
		auto cell = std::make_shared<WorldCell>(Vec3f(x,data->heightmap->getValue(x,y),y));
		data->cells[x][y] = cell; 
		cell->generate();
	}
}

WorldCfgParser::WorldCfgParser(WorldSpaceDataUPtr &world_data)
	: world_data(world_data)
{
}

WorldCfgParser::~WorldCfgParser()
{
}

bool WorldCfgParser::parserNewStruct(TCODParser *parser,const TCODParserStruct *str,const char *name)
{
	std::string str_name = str->getName();
	std::cout << str_name << std::endl;
	if(str_name == "world_type")
	{
		world_data->name = name;
	}
	return true;
}

bool WorldCfgParser::parserFlag(TCODParser *parser,const char *name)
{
	std::string str_name = name;
	if(str_name == "canFastTravel")
		world_data->canFastTravel = true;
	return true;
}

bool WorldCfgParser::parserProperty(TCODParser *parser,const char *name, TCOD_value_type_t vtype, TCOD_value_t value)
{
	std::string str_name = name;
	if(str_name == "seed")
		world_data->seed = value.i;
	else if(str_name == "width")
		world_data->nbCells.x() = value.i;
	else if(str_name == "height")
		world_data->nbCells.y() = value.i;
	else if(str_name == "waterType")
		world_data->waterType = value.i;
	else if(str_name == "waterHeight")
		world_data->waterHeight = value.f;

	return true;
}

bool WorldCfgParser::parserEndStruct(TCODParser *parser,const TCODParserStruct *str, const char *name)
{
	std::string str_name = str->getName();
	
	return true;
}

void WorldCfgParser::error(const char *msg)
{
	std::cout << "WORLD CFG PARSER ERROR: " << msg << std::endl;
}
